package config

import (
	"fmt"
	"sync"

	"github.com/nacos-group/nacos-sdk-go/v2/clients"
	"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
	"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
	"github.com/rs/zerolog"
)

var (
	NacosClient config_client.IConfigClient // 导出全局客户端
)

// 只初始化一次
var initOnce = sync.OnceValues(func() (config_client.IConfigClient, error) {
	logger := zerolog.DefaultContextLogger
	// 1. 必须的鉴权配置
	clientConfig := constant.ClientConfig{
		NamespaceId:         NacosConfig.Client.NamespaceId,         // 命名空间ID（默认public）
		Username:            NacosConfig.Client.Username,            // 必须与服务器配置一致
		Password:            NacosConfig.Client.Password,            // 必须与服务器配置一致
		TimeoutMs:           NacosConfig.Client.TimeoutMs,           // 请求超时时间
		NotLoadCacheAtStart: NacosConfig.Client.NotLoadCacheAtStart, // 启动时不读取本地缓存
	}

	// 2. 服务器地址配置
	serverConfigs := []constant.ServerConfig{
		{
			IpAddr:      NacosConfig.Server.IpAddr,      // 替换为实际Nacos服务器IP
			Port:        NacosConfig.Server.Port,        // 默认端口
			Scheme:      NacosConfig.Server.Scheme,      // 协议类型（http/https）
			ContextPath: NacosConfig.Server.ContextPath, // 根据实际部署调整（无特殊需求保持默认）
		},
	}

	// 3. 创建客户端
	client, err := clients.NewConfigClient(
		vo.NacosClientParam{
			ClientConfig:  &clientConfig,
			ServerConfigs: serverConfigs,
		},
	)
	if err != nil {
		logger.Info().Msgf("客户端创建失败: %v\n（请检查服务器地址和网络）\n", err)
		return nil, err
	}

	logger.Info().Msg("nacos client init success")
	NacosClient = client
	return client, nil
})

// NacosClientInit nacos客户端初始化(线程安全的单例初始化)
//
// 参数:
//
//	config_client: nacos的配置客户端
//
// 返回值:
//
//	error: 错误信息
//
//goland:noinspection ALL
func NacosClientInit() (config_client.IConfigClient, error) {
	return initOnce()
}

// GetConfigFromNacos 从Nacos配置中心获取指定配置并解析为指定类型
// 参数:
//
//	-nacosClient: 已初始化的Nacos配置客户端实例
//	-dataId:      配置的唯一标识ID (对应Nacos控制台的Data ID)
//	-group:       配置所属分组 (默认使用 DEFAULT_GROUP)
//	-format:      配置格式 (支持JSON、YAML、PROPERTIES等，需与Nacos控制台格式一致)
//
// 返回值:
//
//	-*T:         解析后的配置对象指针
//	-error:      错误信息（当配置获取失败或解析失败时返回）
//
// 功能说明:
//  1. 从Nacos获取指定配置内容
//  2. 添加配置变更监听（热更新支持）
//  3. 将配置内容解析为指定结构体类型
//
// 注意:
//   - 当配置获取失败或内容为空时，将直接终止程序(os.Exit(1))
//   - 配置监听为异步操作，变更通知将通过日志输出
//   - 泛型类型T需实现与format格式匹配的结构体字段
func GetConfigFromNacos[T any](client config_client.IConfigClient, dataId, group, format string) (*T, error) {
	logger := zerolog.DefaultContextLogger
	logger.Info().Msgf("使用DataID===%s,Group===%s,读取nacos配置", dataId, group)
	content, err := client.GetConfig(vo.ConfigParam{
		DataId: dataId,
		Group:  group,
	})
	if err != nil {
		logger.Error().Msgf("获取配置失败,DataID==%s, Group==%s, content==%s 错误===%+v", dataId, group, content, err)
		return nil, fmt.Errorf("nacos获取配置失败,err==%+v", err)
	}

	if content == "" {
		logger.Error().Msgf("配置内容为空,请确认Nacos控制台是否存在DataID: %s与Group: %s", dataId, group)
		return nil, fmt.Errorf("nacos配置内容为空请确认Nacos控制台是否存在DataID: %s与Group: %s", dataId, group)
	}

	logger.Info().Msgf("配置获取成功DataID===%s,Group===%s,内容===\n%s", dataId, group, content)

	if err = client.ListenConfig(vo.ConfigParam{
		DataId: dataId,
		Group:  group,
		OnChange: func(namespace, group, dataId, data string) {
			logger.Info().Msgf("[配置变更通知] DataID: %s,Group:%s,新内容:%s", dataId, group, data)
		},
	}); err != nil {
		logger.Error().Err(err).Msg("监听配置失败,不影响已获取的配置")
		return nil, err
	}

	t, err := ParseConfig[T](content, format)
	if err != nil {
		logger.Error().Err(err).Msg("解析配置错误")
		return nil, err
	}
	return t, nil
}
